﻿namespace Microsoft.VisualStudio.TextTemplating.CommandLine
{
    using System;
    using System.Collections;
    using System.Globalization;
    using System.Reflection;
    using System.Text.RegularExpressions;

    internal class StandardOptionParser : OptionParser
    {
        private string commandName;
        private const string NameGroup = "name";
        private static readonly Regex optionRegex = new Regex(@"^(\/|\-)(?<name>[^\+\-]+)(?<plusminus>\+|\-)?$");
        private const string PlusMinusGroup = "plusminus";

        public StandardOptionParser() : this(null)
        {
        }

        public StandardOptionParser(string commandName)
        {
            this.commandName = commandName;
        }

        public override CommandOptionCollection Parse(string[] arguments, OptionSpecificationCollection optionSpecs)
        {
            CommandOptionCollection options = new CommandOptionCollection();
            int num = (this.commandName == null) ? 1 : 0;
            for (int i = num; i < arguments.Length; i++)
            {
                CommandOption option2;
                string n = null;
                string val = null;
                OptionSpecification specification = null;
                Match match = optionRegex.Match(arguments[i]);
                if (match.Success)
                {
                    string partialOptionName = match.Groups["name"].Value;
                    string str4 = match.Groups["plusminus"].Success ? match.Groups["plusminus"].Value : null;
                    n = null;
                    val = null;
                    OptionSpecification[] partial = optionSpecs.GetPartial(partialOptionName);
                    if (partial.Length == 0)
                    {
                        throw new UsageException(string.Format(CultureInfo.CurrentCulture, "{0} is not a valid option.", new object[] { arguments[i] }));
                    }
                    if (partial.Length > 1)
                    {
                        throw new UsageException(string.Format(CultureInfo.CurrentCulture, "{0} is an ambiguous option.", new object[] { arguments[i] }));
                    }
                    specification = partial[0];
                    n = specification.OptionName;
                    if (!specification.IsFinalOption)
                    {
                        if (specification.ValueType != OptionValueType.NoValue)
                        {
                            if ((i + 1) >= arguments.Length)
                            {
                                if (specification.ValueType == OptionValueType.ValueRequired)
                                {
                                    throw new UsageException(string.Format(CultureInfo.CurrentCulture, "No value specified for option {0}", new object[] { arguments[i] }));
                                }
                            }
                            else if (arguments[i + 1].StartsWith("-", StringComparison.OrdinalIgnoreCase) || arguments[i + 1].StartsWith("/", StringComparison.OrdinalIgnoreCase))
                            {
                                if (specification.ValueType != OptionValueType.ValueOptional)
                                {
                                    throw new UsageException(string.Format(CultureInfo.CurrentCulture, "No value specified for option {0}", new object[] { arguments[i] }));
                                }
                            }
                            else
                            {
                                val = arguments[i + 1];
                                i++;
                            }
                        }
                        else if (str4 != null)
                        {
                            val = str4;
                        }
                        goto Label_01F9;
                    }
                    CommandOption option = new CommandOption(specification.OptionName);
                    i++;
                    while (i < arguments.Length)
                    {
                        option.Add(arguments[i]);
                        i++;
                    }
                    options.Add(option);
                    continue;
                }
                n = "";
                val = arguments[i];
            Label_01F9:
                option2 = null;
                option2 = options[n];
                if (option2 == null)
                {
                    option2 = new CommandOption(n);
                    options.Add(option2);
                }
                else if ((specification != null) && !specification.IsMultiValue)
                {
                    throw new UsageException(string.Format(CultureInfo.CurrentCulture, "-{0} is specified more than once.", new object[] { n }));
                }
                if (val != null)
                {
                    option2.Add(val);
                }
            }
            return options;
        }

        public override string ParseCommandName(string[] arguments)
        {
            if (this.commandName != null)
            {
                return this.commandName;
            }
            if ((arguments == null) || (arguments.Length == 0))
            {
                throw new UsageException("No command is specified");
            }
            return arguments[0];
        }

        public override void SetOptionProperty(object command, OptionSpecification optionSpecification, CommandOption option)
        {
            PropertyInfo relatedProperty = optionSpecification.RelatedProperty;
            Type propertyType = optionSpecification.RelatedProperty.PropertyType;
            if (typeof(bool).IsAssignableFrom(propertyType))
            {
                if (((option.Value == null) || (option.Value.Length == 0)) || (option.Value == "+"))
                {
                    relatedProperty.SetValue(command, true, null);
                }
                else if (option.Value == "-")
                {
                    relatedProperty.SetValue(command, false, null);
                }
            }
            else if (typeof(IList).IsAssignableFrom(propertyType))
            {
                IList list = optionSpecification.RelatedProperty.GetValue(command, null) as IList;
                if (list == null)
                {
                    throw new CommandException(string.Format(CultureInfo.CurrentCulture, "The collection property {0} needs to be initialized in the class constructor.", new object[] { optionSpecification.RelatedProperty.Name }));
                }
                Type collectionType = optionSpecification.CollectionType;
                for (int i = 0; i < option.Values.Count; i++)
                {
                    object obj2 = Convert.ChangeType(option.Values[i], collectionType, CultureInfo.CurrentCulture);
                    list.Add(obj2);
                }
            }
            else
            {
                object obj3 = Convert.ChangeType(option.Value, propertyType, CultureInfo.CurrentCulture);
                relatedProperty.SetValue(command, obj3, null);
            }
        }
    }
}

